using System;
using Community.CsharpSqlite;
using tcl.lang;

  class Testing
  {
    /*
    ** 2009 July 17
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains code to implement the "sqlite" test harness
    ** which runs TCL commands for testing the C#-SQLite port.
    **
    ** $Header$
    */
    public static void Main(string[] args)
    {
      // Array of command-line argument strings.
      {
        string fileName = null;

        // Create the interpreter. This will also create the built-in
        // Tcl commands.

        Interp interp = new Interp();

        // Make command-line arguments available in the Tcl variables "argc"
        // and "argv".  If the first argument doesn't start with a "-" then
        // strip it off and use it as the name of a script file to process.
        // We also set the argv0 and TCL.Tcl_interactive vars here.

        if ((args.Length > 0) && !(args[0].StartsWith("-")))
        {
          fileName = args[0];
        }

        TclObject argv = TclList.newInstance();
        argv.preserve();
        try
        {
          int i = 0;
          int argc = args.Length;
          if ((System.Object)fileName == null)
          {
            interp.setVar("argv0", "tcl.lang.Shell", TCL.VarFlag.GLOBAL_ONLY);
            interp.setVar("tcl_interactive", "1", TCL.VarFlag.GLOBAL_ONLY);
          }
          else
          {
            interp.setVar("argv0", fileName, TCL.VarFlag.GLOBAL_ONLY);
            interp.setVar("tcl_interactive", "0", TCL.VarFlag.GLOBAL_ONLY);
            i++;
            argc--;
          }
          for (; i < args.Length; i++)
          {
            TclList.append(interp, argv, TclString.newInstance(args[i]));
          }
          interp.setVar("argv", argv, TCL.VarFlag.GLOBAL_ONLY);
          interp.setVar("argc", System.Convert.ToString(argc), TCL.VarFlag.GLOBAL_ONLY);
        }
        catch (TclException e)
        {
          throw new TclRuntimeError("unexpected TclException: " + e.Message);
        }
        finally
        {
          argv.release();
        }

        // Setup SQLIte specific object for routines
        Sqlite3.Sqlite3_Init(interp);
        Sqlite3.Sqlitetestbackup_Init(interp);
        Sqlite3.Sqliteconfig_Init(interp);
        Sqlite3.Sqlitetest_autoext_Init(interp);
        Sqlite3.Sqlitetest_func_Init(interp);
        Sqlite3.Sqlitetest_hexio_Init(interp);
        Sqlite3.Sqlitetest_malloc_Init(interp);
        Sqlite3.Sqlitetest_mutex_Init(interp);
        Sqlite3.Sqlitetest1_Init(interp);
        Sqlite3.Sqlitetest2_Init(interp);
        Sqlite3.Sqlitetest3_Init(interp);
        Sqlite3.Sqlitetest6_Init(interp);
        Sqlite3.Sqlitetest9_Init(interp);
        Sqlite3.Md5_Init(interp);

        // Normally we would do application specific initialization here.
        // However, that feature is not currently supported.
        // If a script file was specified then just source that file
        // and quit.

        Console.WriteLine("               C#-SQLite version " + Sqlite3.sqlite3_version);
        Console.WriteLine("An independent reimplementation of the SQLite software library");
        Console.WriteLine("==============================================================");
        Console.WriteLine("");

        if ((System.Object)fileName != null)
        {
          try
          {
            interp.evalFile(fileName);
          }
          catch (TclException e)
          {
            TCL.CompletionCode code = e.getCompletionCode();
            if (code == TCL.CompletionCode.RETURN)
            {
              code = interp.updateReturnInfo();
              if (code != TCL.CompletionCode.OK)
              {
                System.Console.Error.WriteLine("command returned bad code: " + code);
                if (tcl.lang.ConsoleThread.debug) System.Diagnostics.Debug.WriteLine("command returned bad code: " + code);
              }
            }
            else if (code == TCL.CompletionCode.ERROR)
            {
              System.Console.Error.WriteLine(interp.getResult().ToString());
              if (tcl.lang.ConsoleThread.debug) System.Diagnostics.Debug.WriteLine(interp.getResult().ToString());
              System.Diagnostics.Debug.Assert(false, interp.getResult().ToString());
            }
            else if (code != TCL.CompletionCode.EXIT)
            {
              System.Console.Error.WriteLine("command returned bad code: " + code);
              if (tcl.lang.ConsoleThread.debug) System.Diagnostics.Debug.WriteLine("command returned bad code: " + code);
            }
          }

          // Note that if the above interp.evalFile() returns the main
          // thread will exit.  This may bring down the VM and stop
          // the execution of Tcl.
          //
          // If the script needs to handle events, it must call
          // vwait or do something similar.
          //
          // Note that the script can create AWT widgets. This will
          // start an AWT event handling thread and keep the VM up. However,
          // the interpreter thread (the same as the main thread) would
          // have exited and no Tcl scripts can be executed.

          interp.dispose();
          Sqlite3.sqlite3_shutdown();

          System.Environment.Exit(0);
        }

        if ((System.Object)fileName == null)
        {
          // We are running in interactive mode. Start the ConsoleThread
          // that loops, grabbing stdin and passing it to the interp.

          ConsoleThread consoleThread = new ConsoleThread(interp);
          consoleThread.IsBackground = true;
          consoleThread.Start();

          // Loop forever to handle user input events in the command line.

          Notifier notifier = interp.getNotifier();
          while (true)
          {
            // process events until "exit" is called.

            notifier.doOneEvent(TCL.ALL_EVENTS);
          }
        }
      }
    }
  }

namespace tcl.lang
{
  class ConsoleThread : SupportClass.ThreadClass
  {
    private class AnonymousClassTclEvent : TclEvent
    {
      public AnonymousClassTclEvent(string command, ConsoleThread enclosingInstance)
      {
        InitBlock(command, enclosingInstance);
      }
      private void InitBlock(string command, ConsoleThread enclosingInstance)
      {
        this.command = command;
        this.enclosingInstance = enclosingInstance;
      }
      private string command;
      private ConsoleThread enclosingInstance;
      public ConsoleThread Enclosing_Instance
      {
        get
        {
          return enclosingInstance;
        }

      }
      public override int processEvent(int flags)
      {

        // See if the command is a complete Tcl command

        if (Interp.commandComplete(command))
        {
          if (tcl.lang.ConsoleThread.debug)
          {
            WriteLine("line was a complete command");
          }

          bool eval_exception = true;
          TclObject commandObj = TclString.newInstance(command);

          try
          {
            commandObj.preserve();
            Enclosing_Instance.interp.recordAndEval(commandObj, 0);
            eval_exception = false;
          }
          catch (TclException e)
          {
            if (tcl.lang.ConsoleThread.debug)
            {
              WriteLine("eval returned exceptional condition");
            }

            TCL.CompletionCode code = e.getCompletionCode();
            switch (code)
            {

              case TCL.CompletionCode.ERROR:

                Enclosing_Instance.putLine(Enclosing_Instance.err, Enclosing_Instance.interp.getResult().ToString());
                break;

              case TCL.CompletionCode.BREAK:
                Enclosing_Instance.putLine(Enclosing_Instance.err, "invoked \"break\" outside of a loop");
                break;

              case TCL.CompletionCode.CONTINUE:
                Enclosing_Instance.putLine(Enclosing_Instance.err, "invoked \"continue\" outside of a loop");
                break;

              default:
                Enclosing_Instance.putLine(Enclosing_Instance.err, "command returned bad code: " + code);
                break;

            }
          }
          finally
          {
            commandObj.release();
          }

          if (!eval_exception)
          {
            if (tcl.lang.ConsoleThread.debug)
            {
              WriteLine("eval returned normally");
            }


            string evalResult = Enclosing_Instance.interp.getResult().ToString();

            if (tcl.lang.ConsoleThread.debug)
            {
              WriteLine("eval result was \"" + evalResult + "\"");
            }

            if (evalResult.Length > 0)
            {
              Enclosing_Instance.putLine(Enclosing_Instance.out_Renamed, evalResult);
            }
          }

          // Empty out the incoming command buffer
          Enclosing_Instance.sbuf.Length = 0;

          // See if the user set a custom shell prompt for the next command

          TclObject prompt;

          try
          {
            prompt = Enclosing_Instance.interp.getVar("tcl_prompt1", TCL.VarFlag.GLOBAL_ONLY);
          }
          catch (TclException e)
          {
            prompt = null;
          }
          if (prompt != null)
          {
            try
            {

              Enclosing_Instance.interp.eval(prompt.ToString(), TCL.EVAL_GLOBAL);
            }
            catch (TclException e)
            {
              Enclosing_Instance.put(Enclosing_Instance.out_Renamed, "% ");
            }
          }
          else
          {
            Enclosing_Instance.put(Enclosing_Instance.out_Renamed, "% ");
          }

          return 1;
        }
        else
        {
          // Interp.commandComplete() returned false

          if (tcl.lang.ConsoleThread.debug)
          {
            WriteLine("line was not a complete command");
          }

          // We don't have a complete command yet. Print out a level 2
          // prompt message and wait for further inputs.

          TclObject prompt;

          try
          {
            prompt = Enclosing_Instance.interp.getVar("tcl_prompt2", TCL.VarFlag.GLOBAL_ONLY);
          }
          catch (TclException)
          {
            prompt = null;
          }
          if (prompt != null)
          {
            try
            {

              Enclosing_Instance.interp.eval(prompt.ToString(), TCL.EVAL_GLOBAL);
            }
            catch (TclException e)
            {
              Enclosing_Instance.put(Enclosing_Instance.out_Renamed, "");
            }
          }
          else
          {
            Enclosing_Instance.put(Enclosing_Instance.out_Renamed, "");
          }

          return 1;
        }
      } // end processEvent method
    }

    // Interpreter associated with this console thread.

    internal Interp interp;

    // Collect the user input in this buffer until it forms a complete Tcl
    // command.

    internal System.Text.StringBuilder sbuf;

    // Used to for interactive input/output

    private Channel out_Renamed;
    private Channel err;

    // set to true to get extra debug output
    public static bool debug = true;

    // used to keep track of wether or not System.in.available() works
    private static bool sysInAvailableWorks = false;

    internal ConsoleThread(Interp i)
    {
      Name = "ConsoleThread";
      interp = i;
      sbuf = new System.Text.StringBuilder(100);

      out_Renamed = TclIO.getStdChannel(StdChannel.STDOUT);
      err = TclIO.getStdChannel(StdChannel.STDERR);
    }
    override public void Run()
    {
      if (debug)
      {
        WriteLine("entered ConsoleThread run() method");
      }


      put(out_Renamed, "% ");

      while (true)
      {
        // Loop forever to collect user inputs in a StringBuffer.
        // When we have a complete command, then execute it and print
        // out the results.
        //
        // The loop is broken under two conditions: (1) when EOF is
        // received inside getLine(). (2) when the "exit" command is
        // executed in the script.

        getLine();
        string command = sbuf.ToString();

        if (debug)
        {
          WriteLine("got line from console");
          WriteLine("\"" + command + "\"");
        }

        // When interacting with the interpreter, one must
        // be careful to never call a Tcl method from
        // outside of the event loop thread. If we did
        // something like just call interp.eval() it
        // could crash the whole process because two
        // threads might write over each other.

        // The only safe way to interact with Tcl is
        // to create an event and add it to the thread
        // safe event queue.

        TclEvent Tevent = new AnonymousClassTclEvent(command, this); // end TclEvent innerclass

        // Add the event to the thread safe event queue
        interp.getNotifier().queueEvent(Tevent, TCL.QUEUE_TAIL);

        // Tell this thread to wait until the event has been processed.
        Tevent.sync();
      }
    }
    private static void WriteLine(string s)
    {
      System.Console.Out.WriteLine(s);
      if (debug) System.Diagnostics.Debug.WriteLine(s);
    }
    private void getLine()
    {
      sbuf.Append(Console.In.ReadLine());
    }
    private void putLine(Channel channel, string s)
    // The String to print.
    {
      try
      {
        channel.write(interp, s);
        channel.write(interp, "\n");
        channel.flush(interp);
      }
      catch (System.IO.IOException ex)
      {
        System.Console.Error.WriteLine("IOException in Shell.putLine()");
        SupportClass.WriteStackTrace(ex, System.Console.Error);
      }
      catch (TclException ex)
      {
        System.Console.Error.WriteLine("TclException in Shell.putLine()");
        SupportClass.WriteStackTrace(ex, System.Console.Error);
      }
    }
    private void put(Channel channel, string s)
    // The String to print.
    {
      try
      {
        channel.write(interp, s);
        channel.flush(interp);
      }
      catch (System.IO.IOException ex)
      {
        System.Console.Error.WriteLine("IOException in Shell.put()");
        SupportClass.WriteStackTrace(ex, System.Console.Error);
      }
      catch (TclException ex)
      {
        System.Console.Error.WriteLine("TclException in Shell.put()");
        SupportClass.WriteStackTrace(ex, System.Console.Error);
      }
    }
    static ConsoleThread()
    {
      {
        try
        {
          // There is no way to tell whether System.in will block AWT
          // threads, so we assume it does block if we can use
          // System.in.available().

          long available = 0;
          // HACK ATK
          // available = System.Console.In.Length - System.Console.In.Position;
          int generatedAux5 = (int)available;
          sysInAvailableWorks = true;
        }
        catch (System.Exception e)
        {
          // If System.in.available() causes an exception -- it's probably
          // no supported on this platform (e.g. MS Java SDK). We assume
          // sysInAvailableWorks is false and let the user suffer ...
        }

        // Sun's JDK 1.2 on Windows systems is screwed up, it does not
        // echo chars to the console unless blocking IO is used.
        // For this reason we need to use blocking IO under Windows.

        if (Util.Windows)
        {
          sysInAvailableWorks = false;
        }
        if (debug)
        {
          WriteLine("sysInAvailableWorks = " + sysInAvailableWorks);
        }
      }
    }
  } // end of class ConsoleThread
}
